package com.egg.common.util;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.StyleSet;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.http.HttpHeaders;
import sun.misc.BASE64Encoder;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * excel工具类
 *
 * @author east
 */
@Slf4j
public class SmallExcelUtil {


    /**
     * 导出
     *
     * @param clazz 对象
     * @param list  数据
     */
    public static void writerOnApiModelProperty(Class clazz, List list) {
        writerOnApiModelProperty(clazz, list, Optional.empty());
    }

    /**
     * 导出
     *
     * @param clazz       对象
     * @param list        数据
     * @param filenameOpt 文件名
     */
    public static void writerOnApiModelProperty(Class clazz, List list, Optional<String> filenameOpt) {
        if (ObjectUtil.isEmpty(list)) {
            list = Collections.emptyList();
        }
        HttpServletResponse response = HttpServletUtil.getHttpServletResponse();
        if (null == response) {
            return;
        }
        ExcelWriter writer = cn.hutool.poi.excel.ExcelUtil.getWriter();
        // 获取当前类字段
        Field[] fields = clazz.getDeclaredFields();
        //设置表头
        Map<String, String> headerMap = new LinkedHashMap<>();
        for (Field field : fields) {
            if (!field.isAccessible()) {
                // 关闭反射访问安全检查，为了提高速度
                field.setAccessible(true);
            }
            boolean annotationPresent = field.isAnnotationPresent(ApiModelProperty.class);
            if (!annotationPresent) {
                continue;
            }
            ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
            /* 字段名,字段中文名(获取实体中@ApiModelProperty注解value的值) */
            writer.addHeaderAlias(field.getName(), annotation.value());
            headerMap.put(field.getName(), annotation.value());
        }
        writer.writeHeadRow(headerMap.values());
        Workbook workbook = writer.getWorkbook();
        StyleSet styleSet = new StyleSet(workbook);
        styleSet.setWrapText();
        writer.setStyleSet(styleSet);
        if (ObjectUtil.isNotEmpty(list)) {
            // 导出数据, 每个sheet最大写入65535
            List<List> cuttingList = ListUtils.partition(list, 65534);
            for (int i = 0; i < cuttingList.size(); i++) {
                writer.setSheet("sheet" + (i + 1));
                writer.write(cuttingList.get(i));
            }
        }
        //设置单元格宽度自适应
        int columnCount = writer.getColumnCount();
        for (Sheet sheet : writer.getSheets()) {
            for (int c = 0; c < columnCount; c++) {
                sheet.autoSizeColumn(c);
            }
        }
        String filename = getFilename(filenameOpt.orElseGet(() -> LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))));
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/vnd.ms-excel");
        //切记attachment;filename=中间不能有空格，不能有\"之类转义双引号，应该是异步请求拆包获取文件名是硬编码的，
        // 如果获取不到显示默认文件名为BoostrapSwaggerUiDownload.txt，但是在原生swagger-ui中是可以解析的
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment;filename=%s.xls", filename));
        ServletOutputStream out;
        try {
            out = response.getOutputStream();
        } catch (IOException e) {
            log.error("excel导出异常:{}", e.getMessage());
            throw new RuntimeException("导出异常");
        }
        writer.flush(out, true);
    }

    /**
     * 中文乱码适配不同浏览器
     */
    public static String getFilename(String fileName) {
        try {
            HttpServletRequest request = HttpServletUtil.getHttpServletRequest();
            if (null == request) {
                return URLEncoder.encode(fileName, "UTF-8");
            }
            String agent = request.getHeader("user-agent");
            if (agent.contains("MSIE")) {
                // IE
                fileName = URLEncoder.encode(fileName, "UTF-8");
                return fileName.replace("+", " ");
            } else if (agent.contains("Firefox")) {
                //火狐
                BASE64Encoder base64Encoder = new BASE64Encoder();
                return "=?UTF-8?B?" + base64Encoder.encode(fileName.getBytes(StandardCharsets.UTF_8)) + "?=";
            } else {
                return URLEncoder.encode(fileName, "UTF-8");
            }
        } catch (Exception e) {

        }
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
    }

    /**
     * @param clazz
     * @param file
     * @param <T>
     * @return
     */
    public static <T> List<T> readOnApiModelProperty(Class clazz, File file) {
        ExcelReader er = cn.hutool.poi.excel.ExcelUtil.getReader(file);
        er.setHeaderAlias(headerAliasMap(clazz));
        return er.readAll(clazz);
    }

    /**
     * 标题别名map
     *
     * @param clazz
     * @return <ApiModelProperty注解value值, 字段名称>
     */
    private static LinkedHashMap<String, String> headerAliasMap(Class clazz) {
        Field[] fields = clazz.getDeclaredFields();
        LinkedHashMap<String, String> headerMap = new LinkedHashMap<>();
        for (Field field : fields) {
            if (!field.isAccessible()) {
                // 关闭反射访问安全检查，为了提高速度
                field.setAccessible(true);
            }
            boolean annotationPresent = field.isAnnotationPresent(ApiModelProperty.class);
            if (!annotationPresent) {
                continue;
            }
            ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
            /* 设置读取别名 ApiModelProperty 中value为读取key */
            headerMap.put(annotation.value(), field.getName());
        }
        return headerMap;
    }

    /**
     * 导入
     *
     * @param clazz
     * @param stream
     * @param <T>
     * @return
     */
    public static <T> List<T> readOnApiModelProperty(Class clazz, InputStream stream) {
        ExcelReader er = cn.hutool.poi.excel.ExcelUtil.getReader(stream);
        er.setHeaderAlias(headerAliasMap(clazz));
        return er.readAll(clazz);
    }


}